"""
样本标记的API
@Time: 2021/2/5 19:55
@Author:
@File: mark_router.py
"""
from fastapi import APIRouter, UploadFile, File, Depends, Body, Path, Query
from fastapi.responses import HTMLResponse
from typing import List, Union, Optional
from elasticsearch import AsyncElasticsearch
from elasticsearch.helpers import async_bulk
import elasticsearch
from pyfuseki import AsyncFuseki
from pyfuseki.term import RDFList
from pyfuseki.utils import RdfUtils
from rdflib import Graph
import httpx
from pydantic import BaseModel, Field

from models.dto import RESTfulModel
from models.dto.mark_dto.fault_card_mark_dto.card import FaultCardArtificialMarkResult
from models.dto.mark_dto.file_upload_dto import UploadResult
from models.normal import FaultInfoCardDataModel, FmeaCardDataModel
from utils.file_scanner import FaultCollectedCardScanner, FmeaCardScanner
from dependencies import verify_token, get_es, get_fuseki
from config import es_conf
from mapper.owl_mapper import RdfPrefix as rp, ObjectProperty as op
from utils import mark_to_rdf

mark_router = APIRouter(tags=['mark'])

fault_card_scanner = FaultCollectedCardScanner()
fmea_card_scanner = FmeaCardScanner()


@mark_router.get("/upload_test/fault_card",
                 summary="文件上传测试页面",
                 description='返回一个HTML页面，可以用此页面测试文件的上传',
                 tags=['test'])
async def send_upload_html():
    content = """
<body>
<form action="/api/mark/upload_file/fault_card" enctype="multipart/form-data" method="post">
<input name="card_files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)



@mark_router.post('/upload_file/fault_card',
                  response_model=RESTfulModel[UploadResult],
                  dependencies=[Depends(verify_token), ],
                  summary='批量上传故障信息采集卡的接口',
                  description='该接口用于接收上传的故障信息采集卡文件，该接口可以通过 /api/mark/upload_test/fault_card 获得测试页面')
async def upload_fault_card(card_files: List[UploadFile] = File(..., description="本次上传的所有故障信息采集卡文件"),
                            es_client: AsyncElasticsearch = Depends(get_es)):

    card_data_list, upload_result = await fault_card_scanner.save_and_extract_all_files(card_files)

    fault_card_docs = [data_model.dict() for data_model in card_data_list]
    for d in fault_card_docs:
        d['_index'] = es_conf.INDEX_NAME

    await async_bulk(es_client, fault_card_docs)
    return RESTfulModel(code=0, data=upload_result)


@mark_router.post('/upload_file/fmea_card',
                  response_model=RESTfulModel[UploadResult],
#                  dependencies=[Depends(verify_token), ],
                  summary='批量上传故障 FMEA 卡的接口',
                  description="该接口用于接收上传的故障FMEA卡文件，该接口可以通过 /api/mark/upload_test/fault_card 获得测试页面")
async def upload_fmea_card(card_files: List[UploadFile] = File(..., description="本次上传的所有故障 FMEA 卡文件"),
                            es_client: AsyncElasticsearch = Depends(get_es)):
    card_data_list, upload_result = await fmea_card_scanner.save_and_extract_all_files(card_files)

    fmea_card_docs = [data_model.dict() for data_model in card_data_list]
    for d in fmea_card_docs:
        d['_index'] = es_conf.INDEX_NAME

    await async_bulk(es_client, fmea_card_docs)
    return RESTfulModel(code=0, data=upload_result)



class CardData(BaseModel):
    id: str = Field(..., title='信息卡的 ID')
    source: Union[FaultInfoCardDataModel, FmeaCardDataModel] = Field(None, title='信息卡中的数据')


@mark_router.get('/card_data/{card_id}',
                 response_model=RESTfulModel[Optional[CardData]],
#                 dependencies=[Depends(verify_token)],
                 summary='根据信息卡的 ID 获取该信息卡的全部信息',
                 description='+ 当找到时，返回存放于 ES 中的全部信息；\n'
                             '+ 若未找到，`data` 部分返回 `None`')
async def get_card_data(card_id: str = Path(..., title='信息卡的 ID'),
                        es_client: AsyncElasticsearch = Depends(get_es)):
    try:
        es_resp: dict = await es_client.get(es_conf.INDEX_NAME, card_id)
    except elasticsearch.NotFoundError:
        return RESTfulModel(code=0, data=None)

    card_data = CardData(id=es_resp.get('_id'),
                         source=es_resp.get('_source'))
    return RESTfulModel(code=0, data=card_data)


@mark_router.post('/mark_fault_card',
                  response_model=RESTfulModel[str],
#                  dependencies=[Depends(verify_token), ],
                  summary='对故障信息卡的标记')
async def mark_fault_card(mark_result: FaultCardArtificialMarkResult = Body(..., title='标记的结果'),
                          card_id: str = Body(..., title='故障信息卡的 id', description='该 id 即卡的信息存于 ES 中的id'),
                          async_fuseki: AsyncFuseki = Depends(get_fuseki)):
    rdf_list: RDFList = list()  # 存放所有 RDF 的列表
    fault_info_table_uri = rp.FacultyInfoTable.val(card_id)
    # 将故障描述部分转化为 RDF
    if mark_result.fault_desc is not None:
        fault_desc_uri = rp.FacultyDescription.uid()
        rdf_list.append((fault_info_table_uri, op.hasFaultyDescription.value, fault_desc_uri))  # 信息表有故障描述
        rdf_list.extend(
            mark_to_rdf.convert_fault_card.convert_fault_desc(mark_result.fault_desc, fault_desc_uri)
        )
    # TODO: 将剩余部分转化为 RDF

    # 将 RDF 数据存入 Jena Fuseki 中
    graph = Graph()
    RdfUtils.add_list_to_graph(graph, rdf_list)
    try:
        await async_fuseki.insert_graph(graph)
    except httpx._exceptions.HTTPStatusError as e:  # HTTP 状态码不正常时
        return RESTfulModel(code=-1, data=str(e))
    return RESTfulModel(code=0, data='success')